package com.chenfan.magic.interpreter;
import com.chenfan.magic.core.IdcEngineEntry;
import com.chenfan.magic.core.MagicLogPublisher;
import com.chenfan.magic.entity.TTLDetailModel;
import com.chenfan.magic.interpreter.classloader.JavaInterpreterClassLoader;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * java动态解释器
 * <p>
 * 示例:
 * String code = "System.out.println(\"Hello World!\"+(13+2*5/3));";
 * code += "for(int i=0;i<" + i + ";i++){";
 * code += " System.out.println(Math.pow(i,2));";
 * code += "}";
 * code += "return scope.get(\"hh\");";
 * run(code, paramMap);
 *
 * @author caohui
 */
@Slf4j
@Component
@AllArgsConstructor
public class JavaInterpreter implements __IInterpreter {

    public static final String TYPE_CODE = "java";
    public static final String ALIAS = "";

    @Qualifier("javaClassLoaderPath")
    private String javaClassLoaderPath;

    /**
     * 动态编译
     *
     * @param code
     * @return
     * @throws Exception
     */
    private synchronized File compile(String code) throws Exception {
        File file = File.createTempFile("JavaRuntime", ".java", new File(javaClassLoaderPath));
        file.deleteOnExit();
        // 获得类名
        String classname = file.getName();
        classname = classname.contains(".") ? classname.split("\\.")[0] : classname;
        code = code.replace("__Java_Interpreter__", classname).toString();

        // 将代码输出到文件
        PrintWriter out = new PrintWriter(new FileOutputStream(file));
        out.println(code);
        out.close();

        // 编译生成的java文件
        String[] cpargs = new String[]{"-d", javaClassLoaderPath, file.getName()};
        System.out.println(System.getProperty("user.dir"));
        //动态编译
        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        int status = javac.run(null, null, null, "-d", javaClassLoaderPath, javaClassLoaderPath + file.getName());
        if (status != 0) {
            throw new Exception("编译失败, 请检查你的代码语法! ");
        }
        return file;
    }

    /**
     * 反射执行
     *
     * @param code
     * @param paramMap
     * @return
     */
    @Override
    public synchronized Object run(Map<String, Object> idcRuntimePool, String code, Map<String, Object> paramMap) {
        TTLDetailModel ttlDetailModel = new TTLDetailModel();
        ttlDetailModel.start();
        Object result = null;

        //1.编译源代码
        File file = null;
        try {
            file = compile(code);
        } catch (Exception e) {
            e.printStackTrace();
            IdcEngineEntry.throwException(idcRuntimePool, null, e);
            MagicLogPublisher.publishInterpreterEvent(TYPE_CODE, code, result, paramMap, ttlDetailModel.end().getTtl() + "", e);
        }

        //2.通过自定义的类加载器装载字节码
        Class cls = null;
        try {
            if (file != null) {
                String classname = file.getName();
                classname = classname.contains(".") ? classname.split("\\.")[0] : classname;
                //声明文件执行完立即删除
                new File(javaClassLoaderPath + classname + ".class").deleteOnExit();
                JavaInterpreterClassLoader loader = new JavaInterpreterClassLoader();
                cls = loader.findClass(idcRuntimePool, javaClassLoaderPath, classname);
            }
        } catch (Exception e) {
            e.printStackTrace();
            IdcEngineEntry.throwException(idcRuntimePool, null, e);
            MagicLogPublisher.publishInterpreterEvent(TYPE_CODE, code, result, paramMap, ttlDetailModel.end().getTtl() + "", e);
        }

        //3.执行编译后的字节码
        try {
            if (cls != null) {
                Method main = cls.getMethod(__IInterpreter.HANDER_NAME, Map.class);
                result = main.invoke(cls, paramMap);
                MagicLogPublisher.publishInterpreterEvent(TYPE_CODE, code, result, paramMap, ttlDetailModel.end().getTtl() + "");
            }
        } catch (Exception e) {
            e.printStackTrace();
            IdcEngineEntry.throwException(idcRuntimePool, null, e);
            MagicLogPublisher.publishInterpreterEvent(TYPE_CODE, code, result, paramMap, ttlDetailModel.end().getTtl() + "", e);
        }
        return result;
    }

    @Override
    public Object run(Map<String, Object> idcRuntimePool, String expression, String defaultValue) {
        return defaultValue;
    }
}

